﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.ServiceModel;
using System.Xml.Serialization;
using System.Xml;
using System.Reflection;

namespace WcfCore
{
	#region Enums
	public enum PerformanceTestScope
	{
		Direct,
		SameAppDomain,
		SameProcess,
		SameMachine,
		Remote
	}

	public enum Bindings
	{
		Remoting,
		nullTransport,
		netTcp,
		netNamedPipe,
		netMsmq,
		basicHttp,
		wsHttp
	}

	public enum ReceiveDataMethods
	{
		ReceiveData,
		ReceiveSC,
		ReceiveAC,
		ReceiveStream,
		ReceiveText,
		ReceiveSTC,
		ReceiveATC,
		ReceiveDataTransactional,
		ReceiveDataThrowException
	}

	public enum CallModes
	{
		OneWay,
		TwoWay
	}

	public enum SerializerFormats
	{
		DefaultSF,
		PORDCSF,
		NDCSF,
		XMLSF
	}
	#endregion

	[Serializable]
	public class WcfTestInput
	{
		#region Statics
		public static string ClientMachineName;
		public static string ServiceMachineName;
		public static string WcfTestInputDir;
		public static string ConfigDir;
		public static string BindingConfigDir;
		public static string MsmqQueueName;
		public static long DefaultNumberOfMessages;
		public static long DefaultNumberOfMessagesInSession;
		public static int WaitTimeBetweenMessagesInMSecs;
		public static int MessageSizeInBytes;
		public static int TestCleanupTimeMSecs;
		public static string PerformanceCounterDir;
		public static string RemotePerformanceCounterFileFullName;
		public static int PerformanceCounterLogInterval;
		public static string PerformanceCounterOutputDir;
		public static string TickCountOutputFilepath;
		public static PerformanceTestScope[] RequestedScopes;
		public static InstanceContextMode[] RequestedInstancingModes;
		public static Bindings[] RequestedBindings;
		public static string[] RequestedBindingConfigTags;
		public static ReceiveDataMethods[] RequestedMethods;
		public static CallModes[] RequestedCallModes;
		public static SerializerFormats[] RequestedSerializers;
		public static int[] RequestedDataSizes;
		public static bool UseUniqueDataInAdvancedContainer;
		public static int RemotingPortNumber = 9000;
		public static int OneWayLatencyTestCount;

		static WcfTestInput()
		{
			ClientMachineName = ConfigurationManager.AppSettings["ClientMachineName"];
			ServiceMachineName = ConfigurationManager.AppSettings["ServiceMachineName"];
			WcfTestInputDir = ConfigurationManager.AppSettings["WcfTestInputDir"];
			ConfigDir = ConfigurationManager.AppSettings["ConfigDir"];
			BindingConfigDir = ConfigurationManager.AppSettings["BindingConfigDir"];
			MsmqQueueName = ConfigurationManager.AppSettings["MsmqQueueName"];
			DefaultNumberOfMessages = Convert.ToInt64(ConfigurationManager.AppSettings["NumberOfMessages"]);
			DefaultNumberOfMessagesInSession = Convert.ToInt64(ConfigurationManager.AppSettings["NumberOfMessagesInSession"]);
			WaitTimeBetweenMessagesInMSecs = Convert.ToInt32(ConfigurationManager.AppSettings["WaitTimeBetweenMessagesInMSecs"]);
			MessageSizeInBytes = Convert.ToInt32(ConfigurationManager.AppSettings["MessageSizeInBytes"]);
			TestCleanupTimeMSecs = Convert.ToInt32(ConfigurationManager.AppSettings["TestCleanupTimeMSecs"]);
			PerformanceCounterDir = ConfigurationManager.AppSettings["PerformanceCounterDir"];
			RemotePerformanceCounterFileFullName = ConfigurationManager.AppSettings["RemotePerformanceCounterFileFullName"];
			PerformanceCounterLogInterval = Convert.ToInt32(ConfigurationManager.AppSettings["PerformanceCounterLogInterval"]);
			PerformanceCounterOutputDir = ConfigurationManager.AppSettings["PerformanceCounterOutputDir"];
			TickCountOutputFilepath = ConfigurationManager.AppSettings["TickCountOutputFilepath"];
			UseUniqueDataInAdvancedContainer = Convert.ToBoolean(ConfigurationManager.AppSettings["UseUniqueDataInAdvancedContainer"]);
			OneWayLatencyTestCount = Convert.ToInt32(ConfigurationManager.AppSettings["OneWayLatencyTestCount"]);

			RequestedScopes = GetRequestedArrayFromEnum<PerformanceTestScope>("RequestedScopes");
			RequestedInstancingModes = GetRequestedArrayFromEnum<InstanceContextMode>("RequestedInstancingModes");
			RequestedBindings = GetRequestedArrayFromEnum<Bindings>("RequestedBindings");
			RequestedMethods = GetRequestedArrayFromEnum<ReceiveDataMethods>("RequestedMethods");
			RequestedCallModes = GetRequestedArrayFromEnum<CallModes>("RequestedCallModes");
			RequestedSerializers = GetRequestedArrayFromEnum<SerializerFormats>("RequestedSerializers");

			string[] requestedBindingConfigTags = ConfigurationManager.AppSettings["RequestedBindingConfigTags"].Split(',');
			List<string> configTags = new List<string>(requestedBindingConfigTags.Length);
			foreach (string configTag in requestedBindingConfigTags)
			{
				configTags.Add(configTag);
			}
			RequestedBindingConfigTags = configTags.ToArray();

			string[] requestedDatasizes = ConfigurationManager.AppSettings["RequestedDataSizes"].Split(',');
			List<int> sizes = new List<int>(requestedDatasizes.Length);
			foreach (string size in requestedDatasizes)
			{
				sizes.Add(Convert.ToInt32(size));
			}
			RequestedDataSizes = sizes.ToArray();
		}

		private static T[] GetRequestedArrayFromEnum<T>(string configName)
		{
			string[] requested = ConfigurationManager.AppSettings[configName].Split(',');
			List<T> rl = new List<T>(requested.Length);
			foreach (string r in requested)
			{
				rl.Add((T)Enum.Parse(typeof(T), r));
			}
			return rl.ToArray();
		}

		private static XmlSerializer _serializerInstance;
		private static XmlSerializer SerializerInstance
		{
			get
			{
				if (_serializerInstance == null)
				{
					_serializerInstance = new XmlSerializer(typeof(WcfTestInput));
				}
				return _serializerInstance;
			}
		}

		public static void Serialize(WcfTestInput input)
		{
			XmlWriter writer = null;
			try
			{
				if (!Directory.Exists(WcfTestInputDir))
				{
					Directory.CreateDirectory(WcfTestInputDir);
				}

				XmlWriterSettings settings = new XmlWriterSettings();
				settings.Indent = true;

				writer = XmlWriter.Create(input.FileFullPath, settings);
				SerializerInstance.Serialize(writer, input);
			}
			finally
			{
				if (writer != null)
				{
					writer.Close();
				}
			}
		}

		public static WcfTestInput Deserialize(string filename)
		{
			XmlReader reader = null;
			try
			{
				reader = XmlReader.Create(filename);
				WcfTestInput input = (WcfTestInput)SerializerInstance.Deserialize(reader);
				return input;
			}
			finally
			{
				if (reader != null)
				{
					reader.Close();
				}
			}
		}
		#endregion

		internal WcfTestInput()
		{
		}

		public WcfTestInput(PerformanceTestScope scope, InstanceContextMode instancingMode, Bindings binding, string bindingConfigTag, CallModes callMode, SerializerFormats serializer)
		{
			_scope = scope;
			_instancingMode = instancingMode;
			_binding = binding;
			_bindingConfigTag = bindingConfigTag;
			_callMode = callMode;
			_serializer = serializer;
		}

		private PerformanceTestScope _scope;
		public PerformanceTestScope Scope
		{
			get
			{
				return _scope;
			}
			set
			{
				_scope = value;
			}
		}

		private InstanceContextMode _instancingMode;
		public InstanceContextMode InstancingMode
		{
			get
			{
				return _instancingMode;
			}
			set
			{
				_instancingMode = value;
			}
		}

		private Bindings _binding;
		public Bindings Binding
		{
			get
			{
				return _binding;
			}
			set
			{
				_binding = value;
			}
		}

		private string _bindingConfigTag;
		public string BindingConfigTag
		{
			get
			{
				return _bindingConfigTag;
			}
			set
			{
				_bindingConfigTag = value;
			}
		}

		private CallModes _callMode;
		public CallModes CallMode
		{
			get
			{
				return _callMode;
			}
			set
			{
				_callMode = value;
			}
		}

		private SerializerFormats _serializer;
		public SerializerFormats Serializer
		{
			get
			{
				return _serializer;
			}
			set
			{
				_serializer = value;
			}
		}

		private ReceiveDataMethods _receiveDataMethod;
		public ReceiveDataMethods ReceiveDataMethod
		{
			get
			{
				return _receiveDataMethod;
			}
			set
			{
				_receiveDataMethod = value;
			}
		}

		private int _dataSize = 10;
		public int DataSize
		{
			get
			{
				return _dataSize;
			}
			set
			{
				_dataSize = value;
			}
		}

		public string Name
		{
			get
			{
				return string.Format("{0}.{1}.{2}.{3}.{4}",
					this.Scope.ToString(),
					this.InstancingMode.ToString(),
					this.Binding.ToString(),
					this.BindingConfigTag,
					this.CallMode.ToString(),
					this.Serializer.ToString());
			}
		}

		public string DisplayName
		{
			get
			{
				return string.Format("{0},{1},{2},{3},{4},{5},{6},{7}.",
					this.Scope.ToString(),
					this.InstancingMode.ToString(),
					this.Binding.ToString(),
					this.BindingConfigTag,
					this.CallMode.ToString(),
					this.Serializer.ToString(),
					this.ReceiveDataMethod.ToString(),
					this.DataSize);
			}
		}

		public string FileFullPath
		{
			get
			{
				string filename = string.Format("{0}.{1}", this.Name, "xml");
				return Path.Combine(WcfTestInput.WcfTestInputDir, filename);
			}
		}

		public string ConfigFileFullPath
		{
			get
			{
				string filename;
				filename = string.Format("{0}.{1}.{2}",
					this.Binding.ToString(),
					this.BindingConfigTag,
					"config");

				return Path.Combine(WcfTestInput.ConfigDir, filename);
			}
		}

		public string BindingConfigFileFullPath
		{
			get
			{
				return Path.Combine(WcfTestInput.BindingConfigDir, string.Format("Bindings.{0}.config", this.BindingConfigTag));
			}
		}

		public string PerformanceCounterFileFullPath
		{
			get
			{
				string filename = string.Format("{0}.Client.PerformanceCounters.txt", this.Scope.ToString());
				return Path.Combine(WcfTestInput.PerformanceCounterDir, filename);
			}
		}

		public string PerformanceCounterOutputFilepath
		{
			get
			{
				string filename = string.Format("{0}.{1}", this.Name, "csv");
				return Path.Combine(WcfTestInput.PerformanceCounterOutputDir, filename);
			}
		}

		public Type ServiceType
		{
			get
			{
				Assembly ass = Assembly.LoadFrom(this.ServiceAssemblyFilename);
				return ass.GetType(ServiceTypeName);
			}
		}

		public Type ServiceProxyType
		{
			get
			{
				Assembly ass = Assembly.LoadFrom(this.ServiceAssemblyFilename);
				return ass.GetType(ServiceProxyTypeName);
			}
		}

		public string ServiceAssemblyFilename
		{
			get
			{
				return string.Format("WcfServices.{0}.{1}.{2}.dll",
					this.InstancingMode.ToString(),
					this.CallMode.ToString(),
					this.Serializer.ToString());
			}
		}

		public string ServiceTypeFullNameAndAssemblyName
		{
			get
			{
				return string.Format("{0}, {1}", ServiceType.FullName, ServiceAssemblyFilename);
			}
		}

		private string ServiceTypeName
		{
			get
			{
				return "WcfServices.WcfService";
			}
		}

		private string ServiceProxyTypeName
		{
			get
			{
				return "WcfServices.Proxies.WcfServiceProxy";
			}
		}

		public string RemotingObjectUri
		{
			get
			{
				return this.ServiceTypeName;
			}
		}

		public string RemotingUrl
		{
			get
			{
				string url;
				if (this.Scope.Equals(PerformanceTestScope.Remote))
				{
					url = string.Format("tcp://{0}:{1}/{2}", WcfTestInput.ServiceMachineName, WcfTestInput.RemotingPortNumber, this.RemotingObjectUri);
				}
				else
				{
					url = string.Format("tcp://{0}:{1}/{2}", WcfTestInput.ClientMachineName, WcfTestInput.RemotingPortNumber, this.RemotingObjectUri);
				}

				return url;
			}
		}

		public bool Transactional
		{
			get
			{
				return this.ReceiveDataMethod.Equals(ReceiveDataMethods.ReceiveDataTransactional);
			}
		}

		private const string _numMsg = ".NumberOfMessages";
		public long NumberOfMessages
		{
			get
			{
				if (ConfigurationManager.AppSettings[this.Name + _numMsg] != null)
				{
					return Convert.ToInt64(ConfigurationManager.AppSettings[this.Name + _numMsg]);
				}
				return WcfTestInput.DefaultNumberOfMessages;
			}
		}

		private const string _numMsgInSession = ".NumberOfMessagesInSession";
		public long NumberOfMessagesInSession
		{
			get
			{
				if (ConfigurationManager.AppSettings[this.Name + _numMsgInSession] != null)
				{
					return Convert.ToInt64(ConfigurationManager.AppSettings[this.Name + _numMsgInSession]);
				}
				return WcfTestInput.DefaultNumberOfMessagesInSession;
			}
		}

		public bool IsIllegalCombination
		{
			get
			{
				// msmq and two way don't mix
				if (this.Binding.Equals(Bindings.netMsmq) && this.CallMode.Equals(CallModes.TwoWay))
				{
					return true;
				}

				// If the binding is remoting, we only test for the default (cos we didn't implement other serializers for remoting)
				if (this.Binding.Equals(Bindings.Remoting) && (!this.Serializer.Equals(SerializerFormats.DefaultSF)))
				{
					return true;
				}

				// For now, we don't support Sessionful queued services yet.
				if (this.Binding.Equals(Bindings.netMsmq) && this.InstancingMode.Equals(InstanceContextMode.PerSession))
				{
					return true;
				}

				// nullTransport only applies for SameAppDomain
				if (this.Binding.Equals(Bindings.nullTransport) && (!this.Scope.Equals(PerformanceTestScope.SameAppDomain)))
				{
					return true;
				}

				return false;
			}
		}
	}
}
